/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package e.odbo.data.dao;

import com.openbravo.data.basic.BasicException;
import com.openbravo.data.loader.*;
import com.openbravo.data.loader.serialize.serializer.SerializerWrite;
import com.openbravo.data.loader.serialize.serializer.write.SerializerWriteParams;
import e.odbo.data.bean.I_BaseBean;
import e.odbo.data.dsl.query.QBF;
import e.odbo.data.dsl.query.QBFParameters;
import com.openbravo.data.loader.sentence.SentenceExec;
import com.openbravo.data.loader.sentence.SentenceExecTransaction;
import com.openbravo.data.loader.sentence.SentenceFind;
import com.openbravo.data.loader.sentence.SentenceList;
import com.openbravo.data.loader.serialize.DataParams;
import com.openbravo.data.loader.serialize.DataWrite;
import e.odbo.data.dao.table.I_TableSentenceBuilder;
import e.odbo.data.dao.table.TableDefinition;
import e.odbo.data.dao.table.TableSentenceBuilderWarp;
import com.openbravo.data.user.I_SaveProvider;
import com.openbravo.data.user.I_ListProvider;
import com.openbravo.data.user.ListProviderCreator;
import com.openbravo.data.user.SaveProvider;
import e.odbo.data.bean.CompositeKey;
import e.odbo.data.bean.IKeyed;
import e.odbo.data.dsl.operation.QueryOperation;
import e.odbo.data.dsl.Select;
import e.odbo.data.service.base.I_BaseBeanService;
import java.util.List;

/**
 * @author Administrator
 */
public abstract class BaseDAO<T extends I_BaseBean> extends ViewDAO<T> {

    protected I_DAOInterceptor[] interceptorList={};

    protected I_TableSentenceBuilder tableBuilder;
    protected TableDefinition table;
    SentenceList listSentence;
    SentenceExec updateSentence;
    SentenceExec deleteSentence;
    SentenceFind findSentence;
    SentenceExec insertSentence;
    SentenceFind existSentence;

    TableSentenceBuilderWarp tableWarp;

    public BaseDAO(EntityManager entityManager){
        super(entityManager.getSession());
        entityManager.addDaoToMap(this);
    }
    
    public BaseDAO(I_Session s) {
        super(s);
    }

    public BaseDAO(I_Session s,I_TableSentenceBuilder tableBuilder){
        super(s);
        setTableBuilder(tableBuilder);
    }

    public void init(I_TableSentenceBuilder tableBuilder,I_DAOInterceptor[] interceptorList){
        setTableBuilder(tableBuilder);
        setInterceptorList(interceptorList);
    }

    public void setInterceptorList(I_DAOInterceptor[] interceptorList){
        if(interceptorList==null)
            this.interceptorList=new I_DAOInterceptor[]{};
        else
           this.interceptorList=interceptorList;
    }

    public T find(IKeyed id) throws BasicException {
        return (T) getFindSentence().find(id);
    }

    public boolean exist(IKeyed id) throws BasicException {
        int count = (Integer) getExistSentence().find(id);
        return count > 0;
    }

    public List<T> list() throws BasicException {
        return getListSentence().list();
    }

    public List<T> list(QueryOperation queryOperation) throws BasicException {
        return getListSentence(queryOperation).list();
    }

    public int update(T t) throws BasicException {
        int ret=0;
        for(I_DAOInterceptor interceptor:interceptorList) {
            if(!interceptor.beforeUpdate(t))
                throw new BasicException("interceptor update false:"+interceptor.getClass().getName());
        }
        ret = getUpdateSentence().exec(t);
        for(I_DAOInterceptor interceptor:interceptorList)
            interceptor.afterUpdate(t);
        return ret;
    }

    public int delete(IKeyed t) throws BasicException {
        int ret=0;
        for(I_DAOInterceptor interceptor:interceptorList) {
            if(!interceptor.beforeDelete(t))
                throw new BasicException("interceptor delete false:"+interceptor.getClass().getName());
        }
        ret = getDeleteSentence().exec(t);
        for(I_DAOInterceptor interceptor:interceptorList)
            interceptor.afterDelete(t);
        return ret;
    }

    public int insert(T t) throws BasicException {
        int ret=0;
        for(I_DAOInterceptor interceptor:interceptorList) {
             if(!interceptor.beforeInsert(t))
                 throw new BasicException("interceptor insert false:"+interceptor.getClass().getName());
        }
        ret = getInsertSentence().exec(t);
        for(I_DAOInterceptor interceptor:interceptorList)
            interceptor.afterInsert(t);
        return ret;
    }

    public int delete(final List<IKeyed> objs) throws BasicException {
        return new SentenceExecTransaction(s) {
            public int execInTransaction(Object params) throws BasicException {
                int i = 0;
                for (IKeyed p : objs) {
                    i = delete(p);
                }
                return i;
            }
        }.exec();
    }

    public int update(final List<T> objs) throws BasicException {
        return new SentenceExecTransaction(s) {
            public int execInTransaction(Object params) throws BasicException {
                int i = 0;
                for (T p : objs) {
                    i = update(p);
                }
                return i;
            }
        }.exec();
    }

    public int insert(final List<T> objs) throws BasicException {
        return new SentenceExecTransaction(s) {
            public int execInTransaction(Object params) throws BasicException {
                int i = 0;
                for (T p : objs) {
                    i = insert(p);
                }
                return i;
            }
        }.exec();
    }

    public int insertOrUpdate(T t) throws BasicException {
        if (t instanceof IKeyed) {
            IKeyed key = (IKeyed) t;
            if (key.getKey() == null || !exist(key))
                return insert(t);
        }
        return update(t);
    }


    public int insertOrUpdate(final List<T> objs) throws BasicException {
        return new SentenceExecTransaction(s) {
            public int execInTransaction(Object params) throws BasicException {
                int i = 0;
                for (T p : objs) {
                    i = insertOrUpdate(p);
                }
                return i;
            }
        }.exec();
    }


    public int clear() throws BasicException {
        return getTableBuilderWarp().getClearSentence().exec();
    }

    /*public boolean befareInsert(T t) throws BasicException {
        *//*if (t instanceof I_AutoGeneratorStringKey) {
            I_AutoGeneratorStringKey i_GeneratorKey = (I_AutoGeneratorStringKey) t;
            if (i_GeneratorKey.getKey() == null) {
                i_GeneratorKey.setKey(UUID.randomUUID().toString());
            }
        }*//*

    }

    public boolean befareUpdate(T t) throws BasicException {

    }

    public boolean befareDelete(IKeyed key) throws BasicException {

    }*/

    public TableSentenceBuilderWarp getTableBuilderWarp() {
        if (table == null) {
            table = getTable();
        }
        if(tableWarp==null){
            tableWarp=TableSentenceBuilderWarp.getInstance(table,tableBuilder);
        }
        return tableWarp;
    }

    public abstract TableDefinition getTable();

    public  I_TableSentenceBuilder getTableBuilder(){
        //if (tableBuilder==null)
        //    throw new BasicException("TableSentenceBuilder is not set,please set it");
        return tableBuilder;
    }

    public void setTableBuilder(I_TableSentenceBuilder builder){
        this.tableBuilder=builder;
    }


    public SerializerWrite<IKeyed> getKeySerializerWrite() {
        return getKeySerializerWrite(0);
    }

    public SerializerWrite<IKeyed> getKeySerializerWrite(final int baseIndex) {
        return new SerializerWrite<IKeyed>() {
            public void writeValues(DataWrite dp, IKeyed obj) throws BasicException {
                Object value = obj.getKey();
                if (value instanceof String) {
                    dp.setString(baseIndex + 1, (String) value);
                } else if (value instanceof Integer) {
                    dp.setInt(baseIndex + 1, (Integer) value);
                } else if (value instanceof CompositeKey) {
                    CompositeKey compositeKey = (CompositeKey) obj.getKey();
                    for (int i = 1; i < compositeKey.size() + 1; i++) {
                        value = compositeKey.getKey(i).getKey();
                        if (value instanceof String) {
                            dp.setString(baseIndex + i, (String) value);
                        } else if (value instanceof Integer) {
                            dp.setInt(baseIndex + i, (Integer) value);
                        }
                    }
                }
            }
        };
    }

    public SerializerWrite<T> getInsertSerializerWrite() {
        return new SerializerWrite<T>() {
            public void writeValues(DataWrite dp, T obj) throws BasicException {
                writeInsertValues(dp, obj);
            }
        };
    }

    public SerializerWrite<T> getUpdateSerializerWrite() {
        return new SerializerWrite<T>() {
            public void writeValues(DataWrite dp, T obj) throws BasicException {
                writeUpdateValues(dp, obj);
            }
        };
    }

    public abstract void writeInsertValues(DataWrite dp, T obj) throws BasicException;


    public void writeUpdateRealValues(DataWrite dp, T obj) throws BasicException {
        writeInsertValues(dp, obj);
    }

    public void writeUpdateValues(DataWrite dp, T obj) throws BasicException {
        writeUpdateRealValues(dp, obj);
        if (obj instanceof IKeyed) {
            Object value = ((IKeyed) obj).getKey();
            int colCount = getTableBuilderWarp().getTable().getUpdateColumnCount() + 1;
            if (value instanceof String) {
                dp.setString(colCount, (String) value);
            } else if (value instanceof Integer) {
                dp.setInt(colCount, (Integer) value);
            } else if (value instanceof CompositeKey) {
                CompositeKey compositeKey = (CompositeKey) ((IKeyed) obj).getKey();
                for (int i = 0; i < compositeKey.size(); i++) {
                    value = compositeKey.getKey(i).getKey();
                    if (value instanceof String) {
                        dp.setString(colCount + i, (String) value);
                    } else if (value instanceof Integer) {
                        dp.setInt(colCount + i, (Integer) value);
                    }
                }
            }
        }
    }

    public I_ListProvider getQuery() {
        return new ListProviderCreator(getListSentence());
    }

    public I_ListProvider getQuery(QueryOperation queryOperation) {
        return new ListProviderCreator(getListSentence(queryOperation));
    }

    public SentenceExec getDeleteSentence() {
        if (deleteSentence == null) {
            deleteSentence = getTableBuilderWarp().getDeleteSentence(getKeySerializerWrite());
        }
        return deleteSentence;
    }

    public SentenceFind getFindSentence() {
        if (findSentence == null) {
            findSentence = getTableBuilderWarp().getFindSentence(this, getKeySerializerWrite());
        }
        return findSentence;
    }

    public SentenceFind getFindByFieldIndexSentence(int index) {
        return getTableBuilderWarp().getFindByFieldIndexSentence(index);
    }

    public SentenceExec getInsertSentence() {
        if (insertSentence == null) {
            insertSentence = getTableBuilderWarp().getInsertSentence(getInsertSerializerWrite());
        }
        return insertSentence;
    }

    public SentenceList getListSentence() {
        if (listSentence == null) {
            listSentence = getTableBuilderWarp().getListSentence(this);
        }
        return listSentence;
    }

    public SentenceList getListSentence(QueryOperation queryOperation) {
        return getTableBuilderWarp().getListSentence(this,queryOperation);
    }

    public SentenceExec getUpdateSentence() {
        if (updateSentence == null) {
            updateSentence = getTableBuilderWarp().getUpdateSentence(getUpdateSerializerWrite());
        }
        return updateSentence;
    }

    public SentenceFind getExistSentence() {
        if (existSentence == null) {
            existSentence = getTableBuilderWarp().getExistSentence(getKeySerializerWrite());
        }
        return existSentence;
    }

    public SentenceFind getExistSentence(String[] fields) {
        return getTableBuilderWarp().getExistSentence(fields);
    }

    public I_SaveProvider getSaveProvider() {
        return new SaveProvider(getUpdateSentence(), getInsertSentence(), getDeleteSentence());
    }

    public boolean canUpdate() {
        return getUpdateSentence() != null;
    }

    public boolean canInsert() {
        return getInsertSentence() != null;
    }

    public boolean canDelete() {
        return getDeleteSentence() != null;
    }

    public List list(I_ParentNode parent) throws BasicException {
        Class realParentClass=transClass(parent.getParentClass());
        String parentFieldName = getTableBuilderWarp().getTable().getParentFieldName(realParentClass);
        if (parentFieldName != null && (parent.getParentKey().getKey() instanceof String))
            return getTableBuilderWarp().getListByParentSentence(parent,realParentClass,this).list(parent.getParentKey().getKey());
        return null;
    }

    public Class transClass(Class in) {
        return in;
    }

    @Override
    public List<T> query(QBFParameters filter) throws BasicException {
        return getTableBuilderWarp().getQuerySentence(filter,this).list(filter);
    }

    @Override
    public List<T> query(QBFParameters filter,QueryOperation queryOperation) throws BasicException {
        return getTableBuilderWarp().getQuerySentence(filter,this,queryOperation).list(filter);
    }

    public int updateByField(final IKeyed key, String field, final Object newValue) throws BasicException {
        final int ind = this.getTableBuilderWarp().getTable().getFieldIndex(field);
        if (ind == -1)
            throw new BasicException("no find this field:" + field);
        return getTableBuilderWarp().getUpdateSentence(new int[]{ind}, SerializerWriteParams.INSTANCE).exec(new DataParams() {
            public void writeValues() throws BasicException {
                getTableBuilderWarp().getTable().getDatas(ind).setValue(dw, 1, newValue);
                getKeySerializerWrite(1).writeValues(dw, key);
            }
        });
    }

    public int updateByFields(final IKeyed key, String[] fields, final Object[] newValue) throws BasicException {
        if (fields.length != newValue.length)
            throw new BasicException("arg error");

        final int[] ind = this.getTableBuilderWarp().getTable().getFieldsIndex(fields);

        return getTableBuilderWarp().getUpdateSentence(ind, SerializerWriteParams.INSTANCE).exec(new DataParams() {
            public void writeValues() throws BasicException {
                for (int i = 0; i < ind.length; i++) {
                    getTableBuilderWarp().getTable().getDatas(ind[i]).setValue(dw, i + 1, newValue[i]);
                }
                getKeySerializerWrite(ind.length).writeValues(dw, key);
            }
        });
    }


    public int delete(QBFParameters filter) throws BasicException {
        return getTableBuilderWarp().getDeleteSentence(filter).exec(filter);
    }

    public boolean isExistByField(String field, Object value) throws BasicException {
        int count = (Integer) getExistSentence(new String[]{field}).find(new Object[]{value});
        return count > 0;
    }

    public boolean isExistByFields(String[] fields, Object[] values) throws BasicException {
        if (fields.length != values.length)
            throw new BasicException("arg error");
        int count = (Integer) getExistSentence(fields).find(values);
        return count > 0;
    }

    public boolean isExist(QBFParameters filter)throws BasicException {
        return count(filter)>0;
    }

    /**
     * public List<T> list(List<IKeyed> keys)throws BasicException{
     * String listSQL=getTableBuilderWarp().getListSQL()+
     * for(IKeyed key:keys){
     * <p>
     * }
     * }
     *
     * @throws BasicException
     */

    public int count(QBFParameters filter) throws BasicException {
        return (Integer) getTableBuilderWarp().getCountSentence(filter).find(filter);
    }

    public int count()throws BasicException {
        return count(QBF.all());
    }

    public T find(QBFParameters filter) throws BasicException {
        List<T> objs=this.query(filter);
        if(objs==null)
            return null;
        if(objs.size()==1)
            return  objs.get(0);
        throw new BasicException("Find too more...");
    }

    public Object findObjectPropVal(String propName, String id) throws BasicException {
        int index = getTableBuilderWarp().getTable().getFieldIndex(propName);
        return getFindByFieldIndexSentence(index).find(new Object[]{id});
    }

    @Override
    public List<T> listBySelect(Select select) throws BasicException {
        return getTableBuilderWarp().getLisBySelectSentence(select,this).list();
    }
}
